import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { Component, Input, TemplateRef, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MenuModule } from './menu.module';
import { MenuItem } from './interface';
import { MenuService } from './menu.service';

const menus = [
  {
    id: '1',
    label: 'Menu 01',
    icon: 'lv-icon-card',
    items: [
      {
        id: '11',
        label: 'Submenu 01',
      },
      {
        id: '12',
        label: 'Submenu 02 long menu text Host-Based Intrusion Detection Service',
      },
      {
        id: '13',
        label: 'Submenu 03',
      },
      {
        id: '14',
        label: 'Submenu 04',
      },
    ],
  },
  {
    id: '2',
    label: 'Menu 02',
    icon: 'lv-icon-list',
  },
  {
    id: '3',
    label: 'Menu 03',
    icon: 'lv-icon-filter',
    disabled: true,
    tips: 'Disabled',
    items: [
      {
        id: '31',
        label: 'Submenu 01',
      },
      {
        id: '32',
        label: 'Submenu 02',
      },
    ],
  },
  {
    id: '4',
    label: 'Menu 04',
    icon: 'lv-icon-link',
    items: [
      {
        id: '41',
        label: 'Submenu 01',
      },
      {
        id: '42',
        label: 'Submenu 02',
      },
    ],
  },
];

@Component({
  template: `
    <lv-menu
      *ngIf="!showLvLabelTemplate"
      [lvMenus]="lvMenus"
      [lvTitle]="lvTitle"
      [lvTheme]="lvTheme"
      [lvCollapsed]="lvCollapsed"
      [lvActiveItemId]="lvActiveItemId"
      [lvItemClickClose]="lvItemClickClose"
      [lvMultiExpansion]="lvMultiExpansion"
      (lvItemClick)="onclick()"
      (lvMenusChange)="onchange()"
    ></lv-menu>
    <lv-menu *ngIf="showLvLabelTemplate" [lvMenus]="lvMenus" [lvLabelTemplate]="labelTpl"></lv-menu>
    <ng-template #labeTpl let-data>
      {{ 'label' + data?.label || '' }}
    </ng-template>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestMenuComponent {
  @Input() lvMenus: MenuItem[];
  @Input() lvTitle: string | TemplateRef<void> = '';
  @Input() lvTheme: 'light' | 'dark' = 'dark';
  @Input() lvCollapsed: boolean = false;
  @Input() lvActiveItemId;
  @Input() showLvLabelTemplate: boolean = false;
  @Input() lvItemClickClose: boolean = true;
  @Input() lvMultiExpansion: boolean;

  @ViewChild('labeTpl', { static: true }) labelTpl: TemplateRef<HTMLElement>;

  onclick() {}

  onchange() {}
}

describe('MenuComponent', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [TestMenuComponent],
      imports: [MenuModule, NoopAnimationsModule],
      providers: [MenuService],
      teardown: { destroyAfterEach: true },
    }).compileComponents;
  }));

  describe('render', () => {
    let component: TestMenuComponent;
    let fixture: ComponentFixture<TestMenuComponent>;

    const renderComponent = param => {
      for (const [key, value] of Object.entries(param)) {
        component[key] = value;
      }
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();
    };

    beforeEach(() => {
      fixture = TestBed.createComponent(TestMenuComponent);
      component = fixture.componentInstance;
    });

    it('with default', () => {
      renderComponent({
        lvMenus: menus,
      });
    });

    it('with dark theme', () => {
      renderComponent({
        lvMenus: menus,
        lvTheme: 'dark',
      });
    });

    it('with lvTitle', () => {
      renderComponent({ lvTitle: 'title' });
      const title = fixture.nativeElement.querySelector('.lv-menu-title');
      expect(title.textContent).toContain('title');
    });
  });

  describe('work', () => {
    let component: TestMenuComponent;
    let fixture: ComponentFixture<TestMenuComponent>;
    let cdr: ChangeDetectorRef;

    const renderComponent = param => {
      for (const [key, value] of Object.entries(param)) {
        component[key] = value;
      }
      fixture.detectChanges();
    };

    const rerenderComponent = param => {
      for (const [key, value] of Object.entries(param)) {
        component[key] = value;
      }
      cdr.detectChanges();
    };

    beforeEach(() => {
      fixture = TestBed.createComponent(TestMenuComponent);
      component = fixture.componentInstance;
      cdr = fixture.debugElement.injector.get(ChangeDetectorRef);
      renderComponent({ lvMenus: menus });
    });

    it('with lvLabelTemplate', () => {
      rerenderComponent({ showLvLabelTemplate: true });
      const menuItems = fixture.nativeElement.querySelector('.lv-menu-item');
      expect(menuItems.querySelector('span').textContent).toContain(`label${menus[0].label}`);
    });

    it('without lvMultiExpansion', () => {
      const menuItems = fixture.nativeElement.querySelectorAll('.level0 > li > .lv-menu-item');
      expect(menuItems[0].className).not.toContain('lv-menu-item-expanded');

      menuItems[0].dispatchEvent(new Event('click'));
      fixture.detectChanges();
      expect(menuItems[0].className).toContain('lv-menu-item-expanded');

      menuItems[3].dispatchEvent(new Event('click'));
      fixture.detectChanges();
      expect(menuItems[0].className).not.toContain('lv-menu-item-expanded');
      expect(menuItems[3].className).toContain('lv-menu-item-expanded');
    });

    it('with lvMultiExpansion', () => {
      rerenderComponent({ lvMultiExpansion: true });
      const menuItems = fixture.nativeElement.querySelectorAll('.level0 > li > .lv-menu-item');
      menuItems[0].dispatchEvent(new Event('click'));
      menuItems[3].dispatchEvent(new Event('click'));
      fixture.detectChanges();

      expect(menuItems[0].className).toContain('lv-menu-item-expanded');
      expect(menuItems[3].className).toContain('lv-menu-item-expanded');
    });

    it('with lvActiveItemId', () => {
      rerenderComponent({ lvActiveItemId: '42' });
      const menusList = fixture.nativeElement.querySelectorAll('.level1');
      const menuItems = menusList[2].querySelectorAll('.lv-menu-item');
      expect(menuItems[1].className).toContain('lv-menu-item-active');
      expect(menuItems[1].className).toContain('lv-menu-item-activable');
    });

    it('with lvCollapsed', () => {
      const menu = fixture.nativeElement.querySelector('.lv-menu');
      expect(menu.className).not.toContain('lv-menu-collapsed');
      rerenderComponent({ lvCollapsed: true });
      expect(menu.className).toContain('lv-menu-collapsed');
    });

    // TODO: 弹层关闭目前存在问题，后续修改；
    it('with lvItemClickClose opened', fakeAsync(() => {
      rerenderComponent({ lvCollapsed: true, lvItemClickClose: true });
      const menuItems = fixture.nativeElement.querySelectorAll('.level0 > li > .lv-menu-item');
      menuItems[0].dispatchEvent(new Event('mouseenter'));
      const subMenu = document.querySelector('.lv-left-menu-panel');
      const subMenuItems = subMenu.querySelectorAll('.lv-menu-item');
      subMenuItems[0].dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      expect(subMenuItems[0].className).toContain('lv-menu-item-active');
    }));
  });
});
